﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RayDen.Library.Core;
using RayDen.Library.Core.Components;
using RayDen.Library.Core.Primitives;
using RayDen.RayEngine;
using RayDen.RayEngine.Core.Types;
using RayEngine.Spectral.Entities;

namespace RayEngine.Spectral.Engine
{
    public class HitManager : Singleton<HitManager>
    {
        //private RayEngineScene scene;
        private SceneManager sceneManager;

        public HitManager() { }
        public HitManager(SceneManager scm)
        {
            sceneManager = scm;
            if (instance!= null && instance.sceneManager == null)
            {
                instance.sceneManager = scm;
            }
        }

        public void EvalHit(ref RayData ray, ref RayHit hit,ref ShadePointInfo point)
        {
            if (point == null)
            {
                point = new ShadePointInfo();
            }
            point.IncomingDirection = -ray.Dir;

            if (hit.Miss())
            {
                point.PointType = ShadePointType.Environment;                
                return;
            }
            point.IncomingDistance = hit.Distance;
            point.U = hit.U;
            point.V = hit.V;
            point.ElementIndex = (int)hit.Index;
            var mesh = sceneManager.GetObject(hit.Index);
            
            point.ObjectId = mesh.ID;

            if (mesh.IsLightsource)
            {
                point.PointType = ShadePointType.Light;
                point.ObjectId = mesh.ChildID;
                return;
            }
            
            point.PointType = ShadePointType.Surface;
            mesh.Mesh.InterpolateTriangleNormal(point.ElementIndex, hit.U, hit.V, ref point.GeoNormal);

            point.ShadingNormal = (Normal.Dot(ref ray.Dir, ref point.GeoNormal) > 0f) ? -point.GeoNormal : point.GeoNormal;
            if (point.Frame == null)
                 point.Frame = new ONB(ref point.GeoNormal);
            else 
                point.Frame.SetFromZ(ref point.GeoNormal);

            point.Diffuse = mesh.Material.Diffuse.Evaluate(hit.U, hit.V);
            point.Specular = mesh.Material.Specular.Evaluate(hit.U, hit.V);
            if (mesh.Material.Glossy != null)
                point.Glossy = mesh.Material.Glossy.Evaluate(hit.U, hit.V);
            if (mesh.Material.Transmission!=null)
                point.Transmittance = mesh.Material.Transmission.Evaluate(hit.U, hit.V);
            point.IndexOfRefraction = mesh.Material.IndexOfRefraction.Sample(420f);
            point.Exponent = mesh.Material.Exponent;

            float bpdf;
            point.Brdf = mesh.Material.GetBrdf(point, 0f, out bpdf);
        }
    }
}
